/*
 *  CPPOutput.cpp
 *  SoundOutputter
 *
 *  Created by Pierre DUCHENE on 10/02/10.
 *  Copyright 2010 Pierre DUCHENE. All rights reserved.
 *
 */

#include "CPPOutput.h"
#include <stdlib.h>
#include <iostream>

using std::cout;
using std::cerr;

static float* buffer = (float*)malloc(kBufferSize * sizeof(float));
unsigned int nbSampleRead = 0;
static unsigned int bufferInputIndex = 0;
static unsigned int bufferOutputIndex = 0;
static AudioUnit output;

#pragma mark -
#pragma mark Memory Management

void AddDataToBuffer(float* data, unsigned int size)
{
	// Add new data to buffer
	unsigned int i;
	for (i = 0; i < size; i++) {
		bufferInputIndex = (bufferInputIndex+1) % kBufferSize;
		buffer[bufferInputIndex] = data[i];
	}
}

int NBSampleRead()
{
	unsigned int resultat = nbSampleRead;
	nbSampleRead = 0;
	return resultat;
}

void ClearBuffer()
{
	for (unsigned int i = 0; i < kBufferSize; i++) {
		buffer[i] = 0;
	}
	bufferInputIndex = 0;
}

void DisplayBuffer()
{
	for (unsigned int i = 0; i < kBufferSize; i++) {
		cout << "\ni:"<< i << " value:" << buffer[i];
	}
}

unsigned int GetSize()
{
	return kBufferSize;
}

unsigned int getbufferOutputIndex(){
	return bufferOutputIndex;
}

float* GetBuffer()
{
	return buffer;
}

float* GetNextValues(int nbValues)
{
	float* result = (float*)malloc(nbValues*sizeof(float));
	unsigned int start = bufferOutputIndex + 100;
	unsigned int stop = (bufferOutputIndex + nbValues) % kBufferSize;
	int i = 0;
	bool finished = false;
	while (!finished) {
		unsigned int now = (start + i) % kBufferSize;
		result[i] = buffer[now];
		i++;
		if (i == stop) {
			finished = true;
		}
	}
	return result;
}

#pragma mark -
#pragma mark Audio Unit Methods
int OpenOutput()
{
	// Get the default output
	ComponentDescription cd;
    cd.componentType = kAudioUnitType_Output;
    cd.componentSubType = kAudioUnitSubType_DefaultOutput;
    cd.componentManufacturer = kAudioUnitManufacturer_Apple;
    cd.componentFlags = 0;
    cd.componentFlagsMask = 0;
    Component comp = FindNextComponent( NULL, &cd );
    if ( comp == NULL )
        return -1;
    
	// open and inti the output
    OSStatus result;
    result = OpenAComponent( comp, &output );
    if ( noErr != result )
        return -1;
	
    result = AudioUnitInitialize( output );
    if ( noErr != result )
        return -1;
	
	// Set the callback function witch called when output need data
    AURenderCallbackStruct callbackData;
    callbackData.inputProc = MyAURenderCallback;
    callbackData.inputProcRefCon = NULL;
    
	result = AudioUnitSetProperty(output, kAudioUnitProperty_SetRenderCallback,kAudioUnitScope_Global,
								  0, &callbackData, sizeof(callbackData));
    if ( result != noErr ){
        return -1;
    }
    
    AudioStreamBasicDescription streamFormat;
	streamFormat.mSampleRate = kDefaultSampleRate;
	streamFormat.mFormatID = kAudioFormatLinearPCM;			
	streamFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
	streamFormat.mBytesPerPacket = kBytesPerPacket;	
	streamFormat.mFramesPerPacket = kFramesPerPacket;	
	streamFormat.mBytesPerFrame = kBytesPerFrame;		
	streamFormat.mChannelsPerFrame = kChannelsPerFrame;		
	streamFormat.mBitsPerChannel = kBitsPerChannel;	
	
	result = AudioUnitSetProperty (output,
								   kAudioUnitProperty_StreamFormat,
								   kAudioUnitScope_Input,
								   0,
								   &streamFormat,
								   sizeof(AudioStreamBasicDescription));
	
    if ( noErr != result ){
        return -1;
    }
	
    return 0;
}

int CloseOutput()
{
    ComponentResult cResult;
    OSErr err;
    
    if ( noErr != ( cResult = AudioUnitUninitialize( output ) ) ){
        return -1;
    }
    
    if ( noErr != ( err = CloseComponent( output ) ) ){
        return -1;
    }
	
    return 0;
}

int Start()
{
	ComponentResult cResult = 0;
    
    if ( noErr != ( cResult = AudioOutputUnitStart( output ) ) ){
        cerr << "AudioOutputUnitStart failed\n";
        cResult = -1;
    }
	return cResult;
}

int Stop()
{
	ComponentResult cResult = 0;
	if ( noErr != ( cResult = AudioOutputUnitStop( output ) ) ){
        cerr << "AudioOutputUnitStop failed\n";
		cResult = -1;
    }
	return cResult;
}

#pragma mark -
#pragma mark CallBack Function
OSStatus MyAURenderCallback (
							 void                        *inRefCon,
							 AudioUnitRenderActionFlags  *ioActionFlags,
							 const AudioTimeStamp        *inTimeStamp,
							 UInt32                      inBusNumber,
							 UInt32                      inNumberFrames,
							 AudioBufferList             *ioData
							 )
{	
	
    Float32 *sample = (Float32*)( ioData->mBuffers[ 0 ].mData );
    //bool out_Before_in = bufferOutputIndex < bufferInputIndex;
	
    for ( int i = 0; i < inNumberFrames; ++i ){
		bufferOutputIndex = (bufferOutputIndex+1) % kBufferSize;
        *sample++ = buffer[bufferOutputIndex];
    }
	
	// Copy on other channel
	for (UInt32 channel = 1; channel < ioData->mNumberBuffers; channel++)
		memcpy (ioData->mBuffers[channel].mData, ioData->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize);
	
	nbSampleRead++;
	
	//bool in_Before_out = bufferOutputIndex > bufferInputIndex;
	
	/*
	if ((bufferOutputIndex + 512 < kBufferSize) && out_Before_in == in_Before_out) {
		cout << nbSampleRead << "passe devant\n";
	}
	 */
	
	
    return noErr;
}
